# -*- coding: utf-8 -*-

import struct
import time
import socket
import threading


class TcpMsgClient(object):

    # 协议: 消息总长(4字节)  消息内容(msg)

    TRY_CONNECT_TIME = 1    # 秒
    MAX_RECV_BUFFER = 65536

    def __init__(self):
        super(TcpMsgClient, self).__init__()
        self._sock = None
        self._server_addr = None
        self._server_port = None
        self._buff = ""
        self._connect_proc = None
        self._is_connected = False
        self._last_connect_time = None
        print("Init a TcpMsgClient instance")

    def setup(self, server_addr, server_port):
        self._server_addr = server_addr
        self._server_port = server_port

    def wait_connected(self, max_wait_time):
        count = max_wait_time / TcpMsgClient.TRY_CONNECT_TIME
        for i in xrange(count):
            if not self._is_connected:
                self.tick()
                time.sleep(TcpMsgClient.TRY_CONNECT_TIME)
            else:
                return True
        print("Connect server FAILED !!!")
        return False

    def tick(self):
        if self._sock is None:
            # print("Tick socket")
            self._init_socket(False)
            self._tick_connect_server()
        elif not self._is_connected:
            # print("Tick connect")
            self._tick_connect_server()
        else:
            # print("Tick recv")
            self._tick_recv_packet()

    def bind_connect_success(self, func):   # 请在run()前绑定好，否则错过事件
        self._connect_proc = func

    def is_connected(self):
        return self._is_connected

    def send(self, msg):
        packet = struct.pack('i', len(msg) + 4) + msg
        print("TcpMsgClient {} send packet: {}".format(self._sock, repr(packet)))
        try:
            self._sock.sendall(packet)
        except socket.error as e:
            print("TcpMsgClient send msg failed: {}".format(e))
            self.close_conn()

    def recv_proc(self, msg):
        pass

    def offline_proc(self):
        pass

    def _init_socket(self, is_block=True):
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
        self._sock.setblocking(is_block)
        print("Init socket success: {}".format(self._sock))

    def _tick_connect_server(self):
        curr_time = time.time()
        # print("Curr time: {}".format(curr_time))
        if self._last_connect_time is not None and curr_time - self._last_connect_time < TcpMsgClient.TRY_CONNECT_TIME:
            return

        self._last_connect_time = curr_time
        err_code = self._sock.connect_ex((self._server_addr, self._server_port))
        if err_code not in [0, 10056]:      # 连接失败
            # print("Can not to connect server, errcode = {}".format(err_code))
            return
        self._is_connected = True

        print("Connect server {}:{} success".format(self._server_addr, self._server_port))
        if self._connect_proc is not None:
            self._connect_proc()

    def _tick_recv_packet(self):
        try:
            packet = self._sock.recv(TcpMsgClient.MAX_RECV_BUFFER)
        except socket.error:
            return
        print("Client tick recv packet: {}".format(repr(packet)))
        self._recv_proc(packet)

    def close_conn(self):
        try:
            self.offline_proc()
        finally:
            self._buff = ""
            self._sock.close()
            self._sock = None
            self._is_connected = False
            self._last_connect_time = None

    def _recv_proc(self, curr_packet):
        full_packet = self._buff + curr_packet

        pos = 0
        while True:
            msg_len = TcpMsgClient._get_msg_len(full_packet, pos)
            if msg_len < 0 or pos + msg_len > len(full_packet):  # 消息不完整
                break
            else:
                self.recv_proc(full_packet[pos + 4: pos + msg_len])
                pos += msg_len
        self._buff = full_packet[pos:]

    @staticmethod
    def _get_msg_len(packet, pos=0):
        if len(packet) < pos + 4:
            return -1
        return struct.unpack('i', packet[pos: pos + 4])[0]

    # def run(self):
    #     self._init_socket()
    #     self._connect_server()
    #     while True:
    #         self._recv_packet()
    #
    # def _connect_server(self):
    #     while True:
    #         try:
    #             server_ip = socket.gethostbyname(self._server_addr)
    #         except socket.gaierror as e:
    #             print("Hostname resolve error: {}".format(e))
    #             time.sleep(TcpMsgClient.TRY_CONNECT_TIME)
    #             continue
    #         try:
    #             self._sock.connect((server_ip, self._server_port))
    #         except socket.error as e:
    #             print('Connect server failed: {}'.format(e))
    #             time.sleep(TcpMsgClient.TRY_CONNECT_TIME)
    #             continue
    #         else:
    #             break
    #     self._is_connected = True
    #     if self._connect_proc is not None:
    #         self._connect_proc()
    #     print("Connect server {}:{} success".format(server_ip, self._server_port))
    #
    # def _recv_packet(self):
    #     try:
    #         packet = self._sock.recv(TcpMsgClient.MAX_RECV_BUFFER)
    #     except socket.error as e:
    #         self.close_conn()
    #         print("Client unexpected closed: {}".format(e))
    #         raise
    #     if not packet:
    #         self.close_conn()
    #         raise Exception("Server close the connection")
    #     print("Client recv packet: {}".format(repr(packet)))
    #     self._recv_proc(packet)




